<!DOCTYPE html> <!-- -*- html -*- -->
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1">
  <title>Span of vectors</title>
  <link rel="shortcut icon" href="img/gatech.gif"/>

  
      <link rel="stylesheet" href="css/demo.css?vers=2759ff">
  

  <style>
      
  .overlay-popup h2 {
      color:      var(--palette-green);
      text-align: center;
  }
  .span-type, #span-type {
      color: var(--palette-violet);
  }
  .complement {
      color: var(--palette-red);
  }
  .inter-is {
      padding: 10px;
  }
  .dg.main .cr.boolean > div > .property-name {
      width: 60%;
  }
  .dg.main .cr.boolean > div > .c {
      width: 40%;
  }
  .dg.main .cr.number.has-slider > div > .property-name {
      width: 10%;
  }
  .dg.main .cr.number.has-slider > div > .c {
      width: 90%;
  }
  .dg.main .cr.number.has-slider > div > .c .slider {
      width: 85%;
  }
  .dg.main .cr.number.has-slider > div > .c input {
      width: 10%;
  }

  </style>

</head>
<body>
    

    
        <script src="js/demo.js?vers=77646a"></script>
    

    <script type="text/javascript">
        "use strict";
        DomReady.ready(function() {

        var is2D, ref;

is2D = ((ref = urlParams.v1) != null ? ref.split(',').length : void 0) === 2;

window.demo = new (is2D ? Demo2D : Demo)({
  mathbox: {
    mathbox: {
      warmup: 10,
      splash: false,
      inspect: false
    }
  }
}, function() {
  var base, base1, base2, base3, base4, base5, base6, base7, checkLabel, clipCube, coeff, complElt, complement, complementColor, diff, eqnElt, gui, guiElts, j, k, l, lColors, lLabels, lVectors, labels, len, len1, len2, linCombo, onDrag, params, popup, range, ref1, ref2, ref3, ref4, ref5, ref6, self, snap, snapThreshold, snapped, spaceElt, spanElt, ss0, ss1, ss2, ss3, str, subspace, subspaces, successElt, surfaceColor, types, updateCaption, updateMesh, vec, vectorsElt, view, zeroThreshold;
  window.mathbox = this.mathbox;
  this.numVecs = 3;
  this.vector1 = this.urlParams.get('v1', 'float[]', [5, 3, -2]);
  this.vector2 = this.urlParams.get('v2', 'float[]', [3, -4, 1]);
  this.vector3 = this.urlParams.get('v3', 'float[]', [-1, 1, 7]);
  this.labels = this.urlParams.get('labels', 'str[]', ['v1', 'v2', 'v3']);
  this.coeffs = this.urlParams.get('coeffs', 'str[]', ['x', 'y', 'z']);
  this.lcstart = this.urlParams.get('lcstart', 'float[]', [1.0, 1.0, 1.0]);
  this.colors = [new Color("blue"), new Color("green"), new Color("brown")];
  this.doTarget = false;
  this.target = null;
  this.targetColor = new Color(0, 0, 0);
  this.doComplement = false;
  this.hideSpace = false;
  if (this.urlParams.v1 != null) {
    this.numVecs = 1;
  }
  if (this.urlParams.v2 != null) {
    this.numVecs = 2;
  }
  if (this.urlParams.v3 != null) {
    this.numVecs = 3;
  }
  if (this.urlParams.target != null) {
    this.target = this.urlParams.get('target', 'float[]');
    this.doTarget = true;
    if ((base = this.urlParams).lincombo == null) {
      base.lincombo = 'on';
    }
    if ((base1 = this.urlParams).captions == null) {
      base1.captions = 'target';
    }
    if ((base2 = this.urlParams).nomove == null) {
      base2.nomove = 'true';
    }
    if ((base3 = this.urlParams).grid == null) {
      base3.grid = 'on';
    }
    this.hideSpace = true;
  } else if (this.urlParams.captions === 'combo') {
    if ((base4 = this.urlParams).lincombo == null) {
      base4.lincombo = 'on';
    }
    if ((base5 = this.urlParams).grid == null) {
      base5.grid = 'on';
    }
    this.hideSpace = true;
  } else if (this.urlParams.captions === 'orthog') {
    if ((base6 = this.urlParams).lincombo == null) {
      base6.lincombo = 'disabled';
    }
    if ((base7 = this.urlParams).grid == null) {
      base7.grid = 'disabled';
    }
    this.doComplement = true;
  }
  this.targetLabel = (ref1 = this.urlParams.tlabel) != null ? ref1 : 'w';
  this.hideSpace = this.urlParams.get('hidespace', 'bool', this.hideSpace);
  this.vectors = [this.vector1, this.vector2, this.vector3].slice(0, this.numVecs);
  this.colors = this.colors.slice(0, this.numVecs);
  this.labels = this.labels.slice(0, this.numVecs);
  this.coeffs = this.coeffs.slice(0, this.numVecs);
  this.lcstart = this.lcstart.slice(0, this.numVecs);
  this.isLive = (this.urlParams.nomove == null) || this.urlParams.nomove === "false";
  if (is2D) {
    ref2 = this.vectors;
    for (j = 0, len = ref2.length; j < len; j++) {
      vec = ref2[j];
      vec[2] = 0;
    }
    if (this.doTarget) {
      this.target[2] = 0;
    }
  }
  params = {
    Axes: this.urlParams.axes === "on"
  };
  checkLabel = "Show " + this.coeffs[0] + "." + this.labels[0];
  if (this.numVecs >= 2) {
    checkLabel += " + " + this.coeffs[1] + "." + this.labels[1];
  }
  if (this.numVecs >= 3) {
    checkLabel += " + " + this.coeffs[2] + "." + this.labels[2];
  }
  params[checkLabel] = this.urlParams.lincombo === "on";
  gui = new dat.GUI({
    width: 350
  });
  gui.closed = this.urlParams.closed != null;
  guiElts = {};
  if (this.urlParams.axes !== "disabled") {
    guiElts.Axes = gui.add(params, 'Axes');
    guiElts.Axes.onFinishChange(function(val) {
      return mathbox.select(".view-axes").set("visible", val);
    });
  }
  if (this.urlParams.lincombo !== "disabled") {
    guiElts[checkLabel] = gui.add(params, checkLabel);
    guiElts[checkLabel].onFinishChange(function(val) {
      return mathbox.select(".lincombo").set("visible", val);
    });
    params[this.coeffs[0]] = this.lcstart[0];
    guiElts[this.coeffs[0]] = gui.add(params, this.coeffs[0], -10, 10).step(0.1);
    if (this.numVecs >= 2) {
      params[this.coeffs[1]] = this.lcstart[1];
      guiElts[this.coeffs[1]] = gui.add(params, this.coeffs[1], -10, 10).step(0.1);
    }
    if (this.numVecs >= 3) {
      params[this.coeffs[2]] = this.lcstart[2];
      guiElts[this.coeffs[2]] = gui.add(params, this.coeffs[2], -10, 10).step(0.1);
    }
  }
  if ((ref3 = this.urlParams.grid) === "enabled" || ref3 === "on") {
    params.Grid = this.urlParams.grid === "on";
    guiElts.Grid = gui.add(params, 'Grid');
    guiElts.Grid.onFinishChange(function(val) {
      mathbox.select("#vecgrid").set("visible", val);
      return clipCube.uniforms.hilite.value = !val;
    });
  }
  view = this.view({
    grid: false,
    axes: this.urlParams.axes !== "disabled",
    axisOpts: {
      zIndex: 1
    }
  });
  this.mathbox.select(".view-axes").set("visible", params.Axes);
  if (this.urlParams.captions === 'orthog') {
    view.array({
      channels: 3,
      width: 1,
      live: false,
      data: [[0, 0, 0]]
    }).point({
      color: "black",
      size: 15,
      zIndex: 3
    });
  }
  lVectors = this.vectors.slice();
  lColors = this.colors.slice();
  lLabels = this.labels.slice();
  if (this.doTarget) {
    lVectors.push(this.target);
    lColors.push(this.targetColor);
    lLabels.push(this.targetLabel);
  }
  this.labeledVectors(view, {
    vectors: lVectors,
    colors: lColors,
    labels: lLabels,
    live: this.isLive,
    zeroPoints: true,
    zeroThreshold: 0.05,
    vectorOpts: {
      zIndex: 2
    },
    labelOpts: {
      zIndex: 3
    },
    zeroOpts: {
      zIndex: 3
    }
  });
  if (this.urlParams.lincombo !== "disabled") {
    linCombo = this.linearCombo(view, {
      coeffs: params,
      coeffVars: this.coeffs,
      vectors: this.vectors,
      colors: this.colors.slice(),
      labels: this.labels
    });
    this.mathbox.select(".lincombo").set("visible", params[checkLabel]);
  }
  surfaceColor = new Color("violet");
  complementColor = new Color("red");
  clipCube = this.clipCube(view, {
    draw: true,
    material: new THREE.MeshBasicMaterial({
      color: surfaceColor.three(),
      opacity: 0.25,
      transparent: true,
      visible: false,
      depthWrite: false,
      depthTest: true
    })
  });
  clipCube.installMesh();
  clipCube.uniforms.hilite.value = !params.Grid;
  range = this.urlParams.get('range', 'float', 10);
  snapThreshold = 1.0 * range / 10.0;
  zeroThreshold = 0.00001;
  subspace = this.subspace({
    vectors: this.vectors,
    noPlane: is2D && this.hideSpace,
    zeroThreshold: zeroThreshold,
    live: this.isLive,
    range: range,
    color: surfaceColor,
    lineOpts: {
      zOrder: 0
    },
    surfaceOpts: {
      zOrder: 1
    }
  });
  subspace.draw(clipCube.clipped);
  if (this.doComplement) {
    complement = this.subspace({
      name: 'complement',
      vectors: subspace.complementFull(is2D),
      noPlane: is2D && this.hideSpace,
      zeroThreshold: zeroThreshold,
      live: this.isLive,
      range: range,
      color: complementColor,
      pointOpts: {
        size: 20,
        zIndex: 4
      },
      lineOpts: {
        zOrder: 0
      },
      surfaceOpts: {
        zOrder: 1
      }
    });
    complement.draw(clipCube.clipped);
  }
  if ((ref4 = this.urlParams.grid) === "enabled" || ref4 === "on") {
    this.grid(clipCube.clipped, {
      vectors: this.vectors,
      live: this.isLive,
      lineOpts: {
        color: surfaceColor
      }
    });
    mathbox.select("#vecgrid").set("visible", this.urlParams.grid === "on");
  }
  snapped = new THREE.Vector3();
  diff = new THREE.Vector3();
  ss0 = this.subspace({
    vectors: [[0, 0, 0]]
  });
  ss1 = this.subspace({
    vectors: [[0, 0, 0]]
  });
  ss2 = this.subspace({
    vectors: [[0, 0, 0], [0, 0, 0]]
  });
  ss3 = this.subspace({
    vectors: [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
  });
  subspaces = [ss0, ss1, ss2, ss3];
  snap = (function(_this) {
    return function(vec, vecs) {
      var ss;
      ss = subspaces[vecs.length];
      if (vecs.length > 0) {
        ss.setVecs(vecs);
      }
      ss.project(vec, snapped);
      diff.copy(vec).sub(snapped);
      if (diff.lengthSq() <= snapThreshold) {
        vec.copy(snapped);
        return true;
      }
      return false;
    };
  })(this);
  self = this;
  onDrag = function(vec) {
    var i, indices, k, l, len1, other, others, ref5, results;
    if (snap(vec, [])) {
      return;
    }
    indices = (function() {
      results = [];
      for (var k = 0, ref5 = self.numVecs; 0 <= ref5 ? k < ref5 : k > ref5; 0 <= ref5 ? k++ : k--){ results.push(k); }
      return results;
    }).apply(this).filter((function(_this) {
      return function(x) {
        return x !== _this.dragging;
      };
    })(this));
    others = (function() {
      var l, len1, results1;
      results1 = [];
      for (l = 0, len1 = indices.length; l < len1; l++) {
        i = indices[l];
        results1.push(self.vectors[i]);
      }
      return results1;
    })();
    for (l = 0, len1 = others.length; l < len1; l++) {
      other = others[l];
      if (snap(vec, [other])) {
        return;
      }
    }
    return snap(vec, others);
  };
  updateMesh = function() {
    var mesh;
    mesh = clipCube.mesh;
    if ((complement != null ? complement.dim : void 0) === 3) {
      mesh.material.color = complementColor;
      return mesh.material.visible = true;
    } else if (subspace.dim === 3) {
      mesh.material.color = surfaceColor.three();
      return mesh.material.visible = true;
    } else {
      return mesh.material.visible = false;
    }
  };
  updateMesh();
  if (this.isLive) {
    this.draggable(view, {
      points: this.vectors,
      onDrag: onDrag,
      postDrag: (function(_this) {
        return function() {
          subspace.setVecs(_this.vectors);
          if (complement != null) {
            complement.setVecs(subspace.complementFull(is2D));
          }
          updateMesh();
          return updateCaption();
        };
      })(this)
    });
  }
  switch (this.urlParams.captions) {
    case "target":
      this.caption('<p>Solve this equation by moving the sliders:</p>\n<p><span id="eqn-here"></span>.</p>');
      eqnElt = document.getElementById('eqn-here');
      popup = this.popup('<h2>Success</h2>\n<p><span id="success-here"></span></p>');
      successElt = document.getElementById('success-here');
      updateCaption = (function(_this) {
        return function() {
          var c, equal, lc, str, str2;
          if (_this.urlParams.capopt === 'matrix') {
            str = _this.texMatrix(_this.vectors, {
              colors: _this.colors.slice()
            });
            str += _this.texVector((function() {
              var k, len1, ref5, results;
              ref5 = this.coeffs;
              results = [];
              for (k = 0, len1 = ref5.length; k < len1; k++) {
                c = ref5[k];
                results.push(params[c]);
              }
              return results;
            }).call(_this));
          } else {
            str = _this.texCombo(_this.vectors, (function() {
              var k, len1, ref5, results;
              ref5 = this.coeffs;
              results = [];
              for (k = 0, len1 = ref5.length; k < len1; k++) {
                c = ref5[k];
                results.push(params[c]);
              }
              return results;
            }).call(_this), {
              colors: _this.colors.slice()
            });
          }
          lc = linCombo.combine();
          str += " = " + _this.texVector(lc);
          equal = lc[0] === _this.target[0] && lc[1] === _this.target[1] && lc[2] === _this.target[2];
          str2 = str;
          str += equal ? " = " : " \\neq ";
          str += _this.texVector(_this.target);
          katex.render(str, eqnElt);
          if (equal) {
            katex.render(str2, successElt);
            return popup.show();
          } else {
            return popup.hide();
          }
        };
      })(this);
      ref5 = this.coeffs;
      for (k = 0, len1 = ref5.length; k < len1; k++) {
        coeff = ref5[k];
        guiElts[coeff].onChange(function() {
          return updateCaption();
        });
      }
      break;
    case "combo":
      this.caption('<p><span id="eqn-here"></span></p>');
      eqnElt = document.getElementById('eqn-here');
      updateCaption = (function(_this) {
        return function() {
          var c, lc, str;
          if (_this.urlParams.capopt === 'matrix') {
            str = _this.texMatrix(_this.vectors, {
              colors: _this.colors.slice()
            });
            str += _this.texVector((function() {
              var l, len2, ref6, results;
              ref6 = this.coeffs;
              results = [];
              for (l = 0, len2 = ref6.length; l < len2; l++) {
                c = ref6[l];
                results.push(params[c]);
              }
              return results;
            }).call(_this));
          } else {
            str = _this.texCombo(_this.vectors, (function() {
              var l, len2, ref6, results;
              ref6 = this.coeffs;
              results = [];
              for (l = 0, len2 = ref6.length; l < len2; l++) {
                c = ref6[l];
                results.push(params[c]);
              }
              return results;
            }).call(_this), {
              colors: _this.colors.slice()
            });
          }
          lc = linCombo.combine();
          str += " = " + _this.texVector(lc);
          return katex.render(str, eqnElt);
        };
      })(this);
      ref6 = this.coeffs;
      for (l = 0, len2 = ref6.length; l < len2; l++) {
        coeff = ref6[l];
        guiElts[coeff].onChange(function() {
          return updateCaption();
        });
      }
      break;
    case "indep":
      this.caption('<p>The set <span id="vectors-here"></span>\n<span class="inter-is">is</span>\n<span id="span-type" class="span-type"></span>.</p>');
      vectorsElt = document.getElementById('vectors-here');
      spanElt = document.getElementById('span-type');
      updateCaption = (function(_this) {
        return function() {
          katex.render(_this.texSet(_this.vectors, {
            colors: _this.colors.slice()
          }), vectorsElt);
          if (subspace.dim === _this.numVecs) {
            return spanElt.innerText = "linearly independent";
          } else {
            return spanElt.innerText = "linearly dependent";
          }
        };
      })(this);
      break;
    case "orthog":
      labels = this.labels.join(', ');
      this.caption("<p>The subspace Span{" + labels + "} <span class=\"inter-is\">is</span>\n<span id=\"span-type\" class=\"span-type\"></span>.</p>\n<p>The orthogonal complement of Span{" + labels + "}\n<span class=\"inter-is\">is</span>\n<span id=\"complement-type\" class=\"span-type complement\"></span>.</p>");
      spaceElt = document.getElementById('span-type');
      complElt = document.getElementById('complement-type');
      types = ["a point", "a line", "a plane", "space"];
      updateCaption = (function(_this) {
        return function() {
          spaceElt.innerText = types[subspace.dim];
          return complElt.innerText = types[complement.dim];
        };
      })(this);
      break;
    default:
      if (this.urlParams.capopt === 'matrix') {
        str = '<p>The span of the columns of <span id="vectors-here"></span>';
      } else {
        str = '<p>Span <span id="vectors-here"></span>';
      }
      this.caption(str + '<span class="inter-is">is</span>\n<span id="span-type"></span>.</p>');
      vectorsElt = document.getElementById('vectors-here');
      spanElt = document.getElementById('span-type');
      updateCaption = (function(_this) {
        return function() {
          if (_this.urlParams.capopt === 'matrix') {
            katex.render(_this.texMatrix(_this.vectors, {
              colors: _this.colors.slice()
            }), vectorsElt);
          } else {
            katex.render(_this.texSet(_this.vectors, {
              colors: _this.colors.slice()
            }), vectorsElt);
          }
          return spanElt.innerText = ["a point", "a line", "a plane", "space"][subspace.dim];
        };
      })(this);
  }
  return updateCaption();
});


        });
    </script>
</body>
</html>

